#!/usr/bin/perl

# $Id: filter.pl 155 2010-06-28 16:38:29Z dolbanavt $
# $Date: 2010-06-28 19:38:35 +0300 (Пнд, 28 Июн 2010) $
# $HeadURL: https://robosearch.googlecode.com/svn/branches/reborn/filter.pl $
# $LastChangedRevision: 345 $

use lib::abs qw( base_lib lib );
use JIP::ToolSet;
use Carp qw( croak );
use Const::Fast qw( const );
use English qw( -no_match_vars );
use Pod::Usage qw( pod2usage );
use Getopt::Long 2.24 qw( :config no_auto_abbrev no_ignore_case );
use File::Spec::Functions qw( catfile );
use FindBin qw( $RealBin );
use Memoize qw( memoize );
use Filter::Utils qw( decode_name decode_path resolve_content_type_for );
use YAML qw( Load );
use IO::YAML;
use Log::Console;

our $VERSION = '1.0';

# more meaningfull names for pod2usage's -verbose parameter
const my $SHOW_USAGE_ONLY         => 0;
const my $SHOW_BRIEF_HELP_MESSAGE => 1;
const my $SHOW_FULL_MANUAL        => 2;

# more meaningfull names for pod2usage's -exitval parameter
const my $RETURN_SUCCESS     => 0;
const my $RETURN_FATAL_ERROR => 1;
const my $RETURN_BAD_OPTIONS => 2;

const my $AUTO_TERMINATED_ENABLED => 1; # bool

#-- ui -------------------------------------------------------------------------
my $options_okay = GetOptions(
    'help'          => \my $is_help_requested,
    'input_file=s'  => \my $path_to_input_file,
    'output_file=s' => \my $path_to_output_file,
    'host_id=i'     => \my $host_id,
    'memoize'       => \my $memoize_requested,
);

# show appropriate help message
pod2usage( -exitval => $RETURN_BAD_OPTIONS, -verbose => $SHOW_USAGE_ONLY )
    unless $options_okay;

pod2usage( -exitval => $RETURN_SUCCESS, -verbose => $SHOW_FULL_MANUAL )
    if $is_help_requested;

# check required options
pod2usage(
    -exitval => $RETURN_BAD_OPTIONS,
    -message => 'required parameter "input_file" not defined',
    -verbose => $SHOW_USAGE_ONLY,
) unless $path_to_input_file;

pod2usage(
    -exitval => $RETURN_BAD_OPTIONS,
    -message => 'required parameter "output_file" not defined',
    -verbose => $SHOW_USAGE_ONLY,
) unless $path_to_output_file;

pod2usage(
    -exitval => $RETURN_BAD_OPTIONS,
    -message => 'required parameter "host_id" not defined',
    -verbose => $SHOW_USAGE_ONLY,
) unless $host_id;

#-- init log -------------------------------------------------------------------
our $log = Log::Console->new( log_level => 'all' );

#-- signal handlers ------------------------------------------------------------
require( catfile( $RealBin, 'signal_handlers.pl' ) );

#-- input object ---------------------------------------------------------------
$log->Note('open yaml stream for reading');

my $yaml_reader = IO::YAML->new(
    'path'            => $path_to_input_file,
    'mode'            => 'r',
);

#-- output object --------------------------------------------------------------
$log->Note('open yaml stream for writing');

my $yaml_writer = IO::YAML->new(
    'path'            => $path_to_output_file,
    'mode'            => 'w',
    'auto_terminated' => $AUTO_TERMINATED_ENABLED,
);

$yaml_writer->handle->binmode(':utf8');

#-- main -----------------------------------------------------------------------
$log->Note('enable memoization') and memoize('decode_path')
    if $memoize_requested;

$log->Note('start filter processing loop...');

while ( defined( my $yaml_item = $yaml_reader->getline() ) ) {
    my $item = Load( $yaml_item );

    $log->Error('item "type" not defined') and next
        if not defined $item->{'type'};

    if ( $item->{'type'} eq 'file' ) {
        for my $field ( qw( name path size ) ) {
            $log->Error( 'item "'. $field .'" not defined' ) and next
                if not defined $item->{ $field };
        }

        $yaml_writer->print( {
            host_id => $host_id,
            type    => 'file',
            name    => decode_name( $item->{'name'} ),
            path    => decode_path( $item->{'path'} ),
            size    => $item->{'size'},
            content => resolve_content_type_for( $item->{'name'} ),
        } ) or croak 'cannot write yaml stream: '. $OS_ERROR;
    }
    elsif ( $item->{'type'} eq 'dir' ) {
        $log->Error('item "path" not defined') and next
            if not defined $item->{'path'};

        my( $dir_name ) = $item->{'path'} =~ m{([^/]+)$}xms;

        $log->Error('directory name not extracted') and next
            if not defined $dir_name;

        $yaml_writer->print( {
            host_id => $host_id,
            type    => 'dir',
            name    => decode_name( $dir_name ),
            path    => decode_path( $item->{'path'} ),
        } ) or croak 'cannot write yaml stream: '. $OS_ERROR;
    }
    else {
        croak 'unexpected value: '. $item->{'type'};
    }
}

$log->Note('done.');
$log->Note('close all yaml streams');

for my $each_fh ( ( $yaml_reader, $yaml_writer ) ) {
    $each_fh->close() or croak 'cannot close yaml stream: '. $OS_ERROR;
}

$log->Note('exiting');

#-- end ------------------------------------------------------------------------

__END__

=head1 NAME

filter.pl - утилита фильтрующая контент генерируемый кроулером

=head1 SYNOPSIS

filter.pl [options]

 Options:
   --help            show full help message
   --input_file      файл созданный кроулером
   --output_file     файл в который записывается фильтрованный контент
   --host_id         идентификатор хоста которому соответствует input_file
   --memoize         включение функции мемоизации (там где много памяти)

=head1 USAGE

# справка
/path/to/filter.pl --help

# фильтрация с мемоизацией
/path/to/filter.pl --input_file=./raw.yaml \
                   --output_file=./filtered.yaml \
                   --host_id=69 \
                   --memoize

=head1 DEPENDENCIES

lib::abs (from CPAN)

Const::Fast (from CPAN)

Memoize (from CPAN)

YAML (from CPAN)

IO::YAML (from CPAN)

JIP::ToolSet (from base_lib)

Log::Console (from base_lib)

=head1 EXIT STATUS

0 - программа выполнена успешно

1 - критическая ошибка программы

2 - неверные параметры командной строки

=head1 AUTHOR

Vladimir Zhavoronkov, <dolbanavt (at) Google Mail>

http://dolbanavt.livejournal.com/

=head1 LICENSE AND COPYRIGHT

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

=cut
